package cn.ibizlab.util.db.dialect;

import cn.ibizlab.util.helper.BeanCache;
import cn.ibizlab.util.security.SpringContextHolder;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;
import com.baomidou.mybatisplus.extension.plugins.pagination.dialects.IDialect;
import com.baomidou.mybatisplus.extension.service.IService;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.OrderByElement;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.util.TablesNamesFinder;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * MYSQL 数据库分页语句组装实现，支持中文排序
 */
public class MySqlDialect implements IDialect {

    private final Class<?>[] ignoreType = new Class<?>[]{Short.class,Integer.class, Long.class, Double.class, Float.class, BigDecimal.class, BigInteger.class, java.util.Date.class, java.sql.Date.class, java.sql.Timestamp.class};

    @Override
    public DialectModel buildPaginationSql(String originalSql, long offset, long limit) {
        if (true) {
            originalSql = processOrder(originalSql);
        }

        StringBuilder sql = new StringBuilder(originalSql).append(" LIMIT ").append(FIRST_MARK);
        if (offset != 0L) {
            sql.append(StringPool.COMMA).append(SECOND_MARK);
            return new DialectModel(sql.toString(), offset, limit).setConsumerChain();
        } else {
            return new DialectModel(sql.toString(), limit).setConsumer(true);
        }
    }

    private String processOrder(String originalSql) {
        try {
            TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
            Statement statement = CCJSqlParserUtil.parse(originalSql);
            Class<?> cls = null;
            String table = null;
            List<String> tables = tablesNamesFinder.getTableList(statement);
            if (tables != null && tables.size() > 0) {
                table = tables.get(0).replace("`", "");
            }
            Map<String, IService> mapperMap = SpringContextHolder.getApplicationContext().getBeansOfType(IService.class);
            for (IService baseMapper : mapperMap.values()) {
                Class<?> domainClass = baseMapper.getEntityClass();
                TableName tableName = domainClass.getAnnotation(TableName.class);
                if (tableName != null && tableName.value() != null && tableName.value().equals(table)) {
                    cls = domainClass;
                    break;
                }
            }
            if (statement instanceof Select) {
                Select select = (Select) statement;
                SelectBody selectBody = select.getSelectBody();
                if (selectBody instanceof PlainSelect) {
                    PlainSelect plainSelect = (PlainSelect) selectBody;
                    if (plainSelect.getOrderByElements() != null) {
                        for (OrderByElement orderByElement : plainSelect.getOrderByElements()) {
                            Boolean ignoreOrder = false;
                            if (cls != null) {
                                for (Field field : cls.getDeclaredFields()) {
                                    TableId tableId = field.getAnnotation(TableId.class);
                                    if (tableId != null && tableId.value().equalsIgnoreCase(String.valueOf(orderByElement.getExpression()).replace("`", "")) && Arrays.asList(ignoreType).contains(field.getGenericType())) {
                                        ignoreOrder = true;
                                    }
                                    TableField tableField = field.getAnnotation(TableField.class);
                                    if (tableField != null && tableField.value().equalsIgnoreCase(String.valueOf(orderByElement.getExpression()).replace("`", "")) && Arrays.asList(ignoreType).contains(field.getGenericType())) {
                                        ignoreOrder = true;
                                    }
                                }
                            }
                            if (!ignoreOrder) {
                                String str = String.format("convert(%s using gbk)", orderByElement.getExpression());
                                orderByElement.setExpression(new Column(str));
                            }
                        }
                        return statement.toString();
                    }
                }
            }
            return originalSql;
        } catch (JSQLParserException e) {
            return originalSql;
        }
    }

}
